// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <zencore/uid.h>
#include <zenhttp/httpcommon.h>

ZEN_THIRD_PARTY_INCLUDES_START
#include <http_parser.h>
ZEN_THIRD_PARTY_INCLUDES_END

#include <atomic>

namespace zen {

class HttpRequestParserCallbacks
{
public:
	virtual ~HttpRequestParserCallbacks() = default;
	virtual void HandleRequest()		  = 0;
	virtual void TerminateConnection()	  = 0;
};

struct HttpRequestParser
{
	explicit HttpRequestParser(HttpRequestParserCallbacks& Connection);
	~HttpRequestParser();

	size_t ConsumeData(const char* InputData, size_t DataSize);
	void   ResetState();

	HttpVerb		 RequestVerb() const { return m_RequestVerb; }
	bool			 IsKeepAlive() const { return m_KeepAlive; }
	std::string_view Url() const { return m_NormalizedUrl.empty() ? std::string_view(m_Url, m_UrlLength) : m_NormalizedUrl; }
	std::string_view QueryString() const { return std::string_view(m_QueryString, m_QueryLength); }
	IoBuffer		 Body() { return m_BodyBuffer; }

	inline HttpContentType ContentType()
	{
		if (m_ContentTypeHeaderIndex < 0)
		{
			return HttpContentType::kUnknownContentType;
		}

		return ParseContentType(m_Headers[m_ContentTypeHeaderIndex].Value);
	}

	inline HttpContentType AcceptType()
	{
		if (m_AcceptHeaderIndex < 0)
		{
			return HttpContentType::kUnknownContentType;
		}

		return ParseContentType(m_Headers[m_AcceptHeaderIndex].Value);
	}

	Oid SessionId() const { return m_SessionId; }
	int RequestId() const { return m_RequestId; }

	std::string_view RangeHeader() const { return m_RangeHeaderIndex != -1 ? m_Headers[m_RangeHeaderIndex].Value : std::string_view(); }

private:
	struct HeaderEntry
	{
		HeaderEntry() = default;

		HeaderEntry(std::string_view InName, std::string_view InValue) : Name(InName), Value(InValue) {}

		std::string_view Name;
		std::string_view Value;
	};

	HttpRequestParserCallbacks& m_Connection;
	char*						m_HeaderCursor			   = m_HeaderBuffer;
	char*						m_Url					   = nullptr;
	size_t						m_UrlLength				   = 0;
	char*						m_QueryString			   = nullptr;
	size_t						m_QueryLength			   = 0;
	char*						m_CurrentHeaderName		   = nullptr;  // Used while parsing headers
	size_t						m_CurrentHeaderNameLength  = 0;
	char*						m_CurrentHeaderValue	   = nullptr;  // Used while parsing headers
	size_t						m_CurrentHeaderValueLength = 0;
	std::vector<HeaderEntry>	m_Headers;
	int8_t						m_ContentLengthHeaderIndex;
	int8_t						m_AcceptHeaderIndex;
	int8_t						m_ContentTypeHeaderIndex;
	int8_t						m_RangeHeaderIndex;
	HttpVerb					m_RequestVerb;
	std::atomic_bool			m_KeepAlive{false};
	bool						m_Expect100Continue = false;
	int							m_RequestId			= -1;
	Oid							m_SessionId{};
	IoBuffer					m_BodyBuffer;
	uint64_t					m_BodyPosition = 0;
	http_parser					m_Parser;
	char						m_HeaderBuffer[1024];
	std::string					m_NormalizedUrl;

	void AppendCurrentHeader();

	int OnMessageBegin();
	int OnUrl(const char* Data, size_t Bytes);
	int OnHeader(const char* Data, size_t Bytes);
	int OnHeaderValue(const char* Data, size_t Bytes);
	int OnHeadersComplete();
	int OnBody(const char* Data, size_t Bytes);
	int OnMessageComplete();

	static HttpRequestParser*	GetThis(http_parser* Parser) { return reinterpret_cast<HttpRequestParser*>(Parser->data); }
	static http_parser_settings s_ParserSettings;
};

}  // namespace zen
